﻿using System;
using System.Linq.Expressions;
using System.Runtime.Serialization;
using System.Text;
using InterLinq.Base;
using InterLinq.Expressions.Helpers;
using InterLinq.Types;

namespace InterLinq.Expressions {
	/// <summary>
	/// A serializable version of <see cref="Expression"/>.
	/// </summary>
	[Serializable]
	[DataContract]
	public abstract class SerializableExpression : ISerializableExpression {
		#region Constructor

		/// <summary>
		/// Default constructor for serialization.
		/// </summary>
		public SerializableExpression() {
		}

		/// <summary>
		/// Constructor with an <see cref="Expression"/> and an <see cref="ExpressionConverter"/>.
		/// </summary>
		/// <param name="expression">The original, not serializable <see cref="Expression"/>.</param>
		/// <param name="expConverter">The <see cref="ExpressionConverter"/> to convert contained <see cref="Expression">Expressions</see>.</param>
		public SerializableExpression(Expression expression, ExpressionConverter expConverter)
			: this(expression.NodeType, expression.Type, expConverter) {
			HashCode = expression.GetHashCode();
		}

		/// <summary>
		/// Constructor with an <see cref="ExpressionType"/>, a <see cref="Type"/> and an <see cref="ExpressionConverter"/>.
		/// </summary>
		/// <param name="nodeType">The <see cref="ExpressionType"/> of the <see cref="Expression"/>.</param>
		/// <param name="type">The <see cref="Type"/> of the <see cref="Expression"/>.</param>
		/// <param name="expConverter">The <see cref="ExpressionConverter"/> to convert contained <see cref="Expression">Expressions</see>.</param>
		private SerializableExpression(ExpressionType nodeType, Type type, ExpressionConverter expConverter)
			: this() {
			m_nodeType = nodeType;
			Type = InterLinqTypeSystem.Instance.GetInterLinqVersionOf<InterLinqType>(type);
		}

		#endregion

		#region Properties

		private ExpressionType m_nodeType;

		/// <summary>
		/// The hashcode of the original <see cref="Expression"/>.
		/// </summary>
		[DataMember]
		public int HashCode { get; set; }

		/// <summary>
		/// See <see cref="Expression.NodeType"/>
		/// </summary>
		[DataMember]
		public ExpressionType NodeType {
			get { return m_nodeType; }
			set { m_nodeType = value; }
		}

		/// <summary>
		/// See <see cref="Expression.Type"/>
		/// </summary>
		[DataMember]
		public InterLinqType Type { get; set; }

		#endregion

		#region ToString() Methods

		/// <summary>
		/// Builds a <see langword="string"/> representing the <see cref="Expression"/>.
		/// </summary>
		/// <returns>A <see langword="string"/> representing the <see cref="Expression"/>.</returns>
		public override string ToString() {
			var sb = new StringBuilder();
			BuildString(sb);
			return sb.ToString();
		}

		/// <summary>
		/// Builds a <see langword="string"/> representing the <see cref="Expression"/>.
		/// </summary>
		/// <param name="builder">A <see cref="System.Text.StringBuilder"/> to add the created <see langword="string"/>.</param>
		internal virtual void BuildString(StringBuilder builder) {
			if (builder == null) {
				throw new ArgumentNullException("builder");
			}
			builder.Append("[");
			builder.Append(m_nodeType.ToString());
			builder.Append("]");
		}

		#endregion

		#region ISerializableExpression Members

		public object Convert(IQueryHandler queryHandler) {
			return ExpressionConversionHelper.Convert(this, queryHandler);
		}

		#endregion
	}
}